package com.mc.fastkit.utils

import android.annotation.SuppressLint
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import android.os.Build
import android.telephony.TelephonyManager
import androidx.annotation.IntDef
import androidx.core.content.getSystemService
import com.mc.fastkit.ext.app
import com.mc.fastkit.ext.cast
import java.net.Inet4Address
import java.net.Inet6Address
import java.net.NetworkInterface
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

/**
 * 网络相关工具类
 * @author MasterChan
 * @date 2021-12-21 14:46
 */
object NetUtils {

    const val NET_NONE = -2
    const val NET_UNKNOWN = -1
    const val NET_ETHERNET = 0
    const val NET_WIFI = 1
    const val NET_2G = 2
    const val NET_3G = 3
    const val NET_4G = 4
    const val NET_5G = 5

    @Retention(AnnotationRetention.SOURCE)
    @IntDef(NET_NONE, NET_UNKNOWN, NET_ETHERNET, NET_WIFI, NET_2G, NET_3G, NET_4G, NET_5G)
    annotation class NetType

    /**
     * 是否连接到可用的网络，如果连接的网络无法访问互联网也会返回false
     * @return Boolean
     */
    @SuppressLint("MissingPermission")
    @JvmStatic
    fun isConnected(): Boolean {
        val manager = app.getSystemService<ConnectivityManager>() ?: return false
        val capabilities = manager.getNetworkCapabilities(manager.activeNetwork)
        return capabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) == true
    }

    /**
     * 获取网络连接类型
     * @return [NetType]
     */
    @SuppressLint("MissingPermission")
    @JvmStatic
    fun getConnectType(): Int {
        val manager = app.getSystemService(ConnectivityManager::class.java)
        val cap = manager.getNetworkCapabilities(manager.activeNetwork) ?: return NET_NONE
        return when {
            cap.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> NET_WIFI
            cap.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> NET_ETHERNET
            cap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> getMobileType(app)
            else -> NET_NONE
        }
    }

    @SuppressLint("MissingPermission")
    @JvmStatic
    fun getMobileType(context: Context): Int {
        val manager = context.getSystemService(TelephonyManager::class.java) ?: return NET_UNKNOWN
        val netWorkType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            manager.dataNetworkType
        } else {
            manager.networkType
        }
        return when (netWorkType) {
            TelephonyManager.NETWORK_TYPE_GPRS,
            TelephonyManager.NETWORK_TYPE_EDGE,
            TelephonyManager.NETWORK_TYPE_CDMA,
            TelephonyManager.NETWORK_TYPE_1xRTT,
            TelephonyManager.NETWORK_TYPE_IDEN -> NET_2G

            TelephonyManager.NETWORK_TYPE_UMTS,
            TelephonyManager.NETWORK_TYPE_EVDO_0,
            TelephonyManager.NETWORK_TYPE_EVDO_A,
            TelephonyManager.NETWORK_TYPE_EVDO_B,
            TelephonyManager.NETWORK_TYPE_HSPA,
            TelephonyManager.NETWORK_TYPE_HSPAP,
            TelephonyManager.NETWORK_TYPE_HSDPA,
            TelephonyManager.NETWORK_TYPE_HSUPA,
            TelephonyManager.NETWORK_TYPE_EHRPD -> NET_3G

            TelephonyManager.NETWORK_TYPE_LTE, 19 -> NET_4G
            TelephonyManager.NETWORK_TYPE_NR -> NET_5G
            TelephonyManager.NETWORK_TYPE_UNKNOWN -> NET_UNKNOWN
            else -> NET_UNKNOWN
        }
    }

    /**
     * 是否是移动网络连接
     * @return Boolean
     */
    @SuppressLint("MissingPermission")
    @JvmStatic
    fun isMobileConnected(): Boolean {
        val manager = app.getSystemService(ConnectivityManager::class.java) ?: return false
        val networkCapabilities = manager.getNetworkCapabilities(manager.activeNetwork)
        return networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true
    }

    @SuppressLint("MissingPermission")
    @JvmStatic
    fun isWifiEnabled(): Boolean {
        val manager = app.getSystemService(WifiManager::class.java) ?: return false
        return manager.isWifiEnabled
    }

    @SuppressLint("MissingPermission")
    @JvmStatic
    fun isWifiConnected(): Boolean {
        val manager = app.getSystemService(ConnectivityManager::class.java) ?: return false
        val networkCapabilities = manager.getNetworkCapabilities(manager.activeNetwork)
        if (networkCapabilities != null) {
            return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
        }
        return false
    }

    @SuppressLint("MissingPermission")
    @JvmStatic
    fun getWifiAddress(): String {
        val wifiManager = app.getSystemService(WifiManager::class.java)
        val ipInt = wifiManager?.connectionInfo?.ipAddress ?: return ""
        return (ipInt and 0xFF).toString()
            .plus(".")
            .plus(ipInt shr 8 and 0xFF)
            .plus(".")
            .plus(ipInt shr 16 and 0xFF)
            .plus(".")
            .plus(ipInt shr 24 and 0xFF)
    }

    @SuppressLint("MissingPermission")
    suspend fun getWifiSsid(): String {
        return suspendCoroutine { getWifiSsid { ssid -> it.resume(ssid) } }
    }

    @SuppressLint("MissingPermission")
    fun getWifiSsid(callback: (String) -> Unit) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val request = NetworkRequest.Builder()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .build()
            val manager = app.getSystemService(ConnectivityManager::class.java)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                runCatching {
                    manager.registerNetworkCallback(request,
                        object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
                            override fun onCapabilitiesChanged(
                                network: Network,
                                networkCapabilities: NetworkCapabilities
                            ) {
                                manager.unregisterNetworkCallback(this)
                                val wifiInfo = networkCapabilities.transportInfo?.cast<WifiInfo>()
                                val ssid = wifiInfo?.ssid
                                if (ssid == WifiManager.UNKNOWN_SSID) {
                                    callback.invoke("")
                                } else {
                                    callback.invoke(ssid?.replace("\"", "") ?: "")
                                }
                            }
                        })
                }.getOrNull()?: callback.invoke(getWifiSsidOld())
            } else {
                runCatching {
                    manager.registerNetworkCallback(request,
                        object : ConnectivityManager.NetworkCallback() {
                            override fun onCapabilitiesChanged(
                                network: Network,
                                networkCapabilities: NetworkCapabilities
                            ) {
                                manager.unregisterNetworkCallback(this)
                                val wifiInfo = networkCapabilities.transportInfo?.cast<WifiInfo>()
                                val ssid = wifiInfo?.ssid
                                if (ssid == WifiManager.UNKNOWN_SSID || ssid.isNullOrEmpty()) {
                                    callback.invoke(getWifiSsidOld())
                                } else {
                                    callback.invoke(ssid.replace("\"", ""))
                                }
                            }
                        })
                }.getOrNull() ?: callback.invoke(getWifiSsidOld())
            }
        } else {
            callback.invoke(getWifiSsidOld())
        }
    }

    private fun getWifiSsidOld(): String {
        val wifiManager = app.getSystemService(WifiManager::class.java)
        val ssid = wifiManager.connectionInfo.ssid
        return if (ssid == WifiManager.UNKNOWN_SSID || ssid.isNullOrEmpty()) {
            ""
        } else {
            ssid.replace("\"", "")
        }
    }

    @SuppressLint("MissingPermission")
    @JvmStatic
    fun getIpAddress(useIpv6: Boolean = false): String {
        val nis = NetworkInterface.getNetworkInterfaces()
        while (nis.hasMoreElements()) {
            val element = nis.nextElement()
            val inetAddresses = element.inetAddresses
            while (inetAddresses.hasMoreElements()) {
                val nextElement = inetAddresses.nextElement()
                if (nextElement.isLoopbackAddress) continue
                val hostAddress = nextElement.hostAddress ?: continue
                //ipv6
                if (useIpv6 && nextElement is Inet6Address) {
                    return if (hostAddress.contains("%")) {
                        if (hostAddress.split("%")[1].contains("wlan0")) {
                            hostAddress.substringBefore("%")
                        } else {
                            continue
                        }
                    } else {
                        hostAddress
                    }
                }
                //ipv4
                if (!useIpv6 && nextElement is Inet4Address) {
                    return hostAddress
                }
            }
        }
        return ""
    }
}